/*
 * 版权所有 (C) 2015 知启蒙(ZHIQIM) 保留所有权利。[遇见知启蒙，邂逅框架梦]
 * 
 * https://zhiqim.org/project/zhiqim_framework/zhiqim_httpd.htm
 *
 * Zhiqim Httpd is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
package org.zhiqim.httpd.context;

import java.util.ArrayList;
import java.util.List;

import org.zhiqim.httpd.HttpContext;
import org.zhiqim.httpd.HttpdConstants;
import org.zhiqim.httpd.websocket.HttpWebSocket;
import org.zhiqim.httpd.websocket.HttpWebSocketManager;
import org.zhiqim.httpd.websocket.HttpWebSocketService;
import org.zhiqim.kernel.model.maps.HashMapSV;
import org.zhiqim.kernel.schedule.Interval;
import org.zhiqim.kernel.schedule.Task;
import org.zhiqim.kernel.util.Asserts;
import org.zhiqim.kernel.util.Lists;
import org.zhiqim.kernel.websocket.WebSocketConstants;

/**
 * ZML对Websocket的管理器
 *
 * @version v1.0.0 @author Zouzhigang 2018-9-27 新建与整理
 */
public class ZmlWebSocketManager implements HttpWebSocketManager, Task, WebSocketConstants, HttpdConstants
{
    private final ZmlContext context;
    private final HashMapSV<List<HttpWebSocket>> websocketMap;
    private Interval interval;
    
    public ZmlWebSocketManager(ZmlContext context)
    {
        this.context = context;
        this.websocketMap = new HashMapSV<>();
    }
    
    @Override
    public void open()
    {//定义11秒检查一次，保证最小1分钟清除即可，太频繁也不大合适
        this.interval = Interval.shedule(this, 11 * 1000);
    }

    @Override
    public void close()
    {
        if (this.interval != null)
        {
            this.interval.close();
            this.interval = null;
        }
    }
    
    @Override
    public HttpContext getContext()
    {
        return context;
    }
    
    @Override
    public HttpWebSocketService getWebSocketService(String protocol)
    {
        return context.getConfig().getWebSocketService(protocol);
    }

    @Override /** 获取协议下的连接列表 */
    public List<HttpWebSocket> get(String protocol)
    {
        if (protocol == null)
            protocol = _DEFAULT_WEB_SOCKET_PROTOCOL_;
        
        List<HttpWebSocket> list = websocketMap.get(protocol);
        if (list == null || list.isEmpty())
            return new ArrayList<>();
        
        return Lists.copy(list, true);
    }
    
    @Override /** 获取协议下指定会话编号的连接列表 */
    public List<HttpWebSocket> get(String protocol, String sessionId)
    {
        if (sessionId == null)
            return get(protocol);
        
        if (protocol == null)
            protocol = _DEFAULT_WEB_SOCKET_PROTOCOL_;
        
        List<HttpWebSocket> list = websocketMap.get(protocol);
        if (list == null || list.isEmpty())
            return new ArrayList<>();

        List<HttpWebSocket> sList = new ArrayList<>();
        synchronized (list)
        {
            for (HttpWebSocket websocket : list)
            {
                if (sessionId.equals(websocket.getSessionId()))
                    sList.add(websocket);
            }
        }
        
        return Lists.trim(sList);
    }

    @Override /** 获取协议下某个参数对应的值的连接 */
    public HttpWebSocket get(String protocol, String paramName, String paramValue)
    {
        Asserts.as(paramName != null?null:"通过参数获取连接时参数名不允许为null");
        Asserts.as(paramValue != null?null:"通过参数获取连接时参数值不允许为null");
        
        if (protocol == null)
            protocol = _DEFAULT_WEB_SOCKET_PROTOCOL_;
        
        List<HttpWebSocket> list = websocketMap.get(protocol);
        if (list == null || list.isEmpty())
            return null;
        
        synchronized (list)
        {
            for (HttpWebSocket websocket : list)
            {
                if (paramValue.equals(websocket.getParameter(paramName)))
                    return websocket;
            }
        }
        
        return null;
    }
    
    /*******************************************************************************/
    //增加 & 删除
    /*******************************************************************************/
    
    @Override /** 由HttpWebsocketService增加连接关联到会话 */
    public void add(HttpWebSocket websocket)
    {
        String protocol = websocket.getProtocol();
        List<HttpWebSocket> list = websocketMap.get(protocol);
        if (list == null)
        {
            synchronized (websocketMap)
            {
                list = websocketMap.get(protocol);
                if (list == null)
                {//双重查询
                    list = new ArrayList<>();
                    websocketMap.put(protocol, list);
                }
            }
        }
        
        synchronized (list)
        {
            list.add(websocket);
        }
    }
    
    @Override /** 从连接中收到结束取消关联会话 */
    public void remove(HttpWebSocket websocket)
    {
        String protocol = websocket.getProtocol();
        List<HttpWebSocket> list = websocketMap.get(protocol);
        if (list == null)
            return;
        
        synchronized (list)
        {
            list.remove(websocket);
        }
    }
    
    /*******************************************************************************/
    //定时检查
    /*******************************************************************************/
    
    @Override
    public void execute()
    {
        //1.找出总列表
        List<List<HttpWebSocket>> all = new ArrayList<>();
        synchronized (websocketMap)
        {
            for (List<HttpWebSocket> list : websocketMap.values())
            {
                all.add(list);
            }
        }
        
        for (List<HttpWebSocket> aList : all)
        {
            //2.查出需要关闭的连接
            List<HttpWebSocket> closes = new ArrayList<>();
            List<HttpWebSocket> list = Lists.copy(aList, true);
            for (HttpWebSocket websocket : list)
            {
                if (!websocket.sendPing())
                {//发送PING失败，异常连接关闭
                    closes.add(websocket);
                }
            }
            list.clear();
            
            //3.逐个关闭连接
            for (HttpWebSocket websocket : closes)
            {
                websocket.close(CLOSE_HANDSHAKE);
            }
            closes.clear();
        }
    }
}
